﻿using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace M2MqttNetDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //MqttClient client = new MqttClient("127.0.0.1", 1883, true, null, null, MqttSslProtocols.TLSv1_2);

            //Console.WriteLine("Hello, World!");
            var dictionary = new Dictionary<Point, int>()
            {
                { new Point(1.0, 1.0), 1 },
                { new Point(2.0, 2.0), 2 },
                { new Point(3.0, 3.0), 3 }
            };
            try
            {
                var options = new JsonSerializerOptions
                {
                    WriteIndented = true,
                    Converters = { new PointConverter(), new PointKeyedDictionaryConverter<int>() }
                };

                var json = JsonSerializer.Serialize(dictionary, options);

                Console.WriteLine(json);
                
                var dictionary2 = JsonSerializer.Deserialize<Dictionary<Point, int>>(json, options)!;

                Debug.Assert(dictionary2[new Point(1.0, 1.0)] == 1);
                Debug.Assert(dictionary2[new Point(2.0, 2.0)] == 2);
                Debug.Assert(dictionary2[new Point(3.0, 3.0)] == 3);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }
    }
    [TypeConverter(typeof(PointTypeConverter))]
    public readonly record struct Point(double X, double Y)
    {
        public override string ToString() => $"({X},{Y})";
        public static Point Parse(string s) 
        {
            var tokens = s.Trim('(', ')').Split(',', StringSplitOptions.TrimEntries);
            if (tokens.Length != 2) 
            {
                throw new FormatException("Invalid format");
            }
            return new Point(double.Parse(tokens[0]), double.Parse(tokens[1]));
        }
    }

    public class PointConverter : JsonConverter<Point> 
    {
        public override Point Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
            => Point.Parse(reader.GetString()!);

        public override void Write(Utf8JsonWriter writer, Point value, JsonSerializerOptions options)
            => writer.WriteStringValue(value.ToString());
    }

    public class PointTypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
            => sourceType == typeof(string);
        public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
            => destinationType == typeof(string);
        public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
            => Point.Parse((string)value);
        public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
            => value?.ToString();
    }

    public class PointKeyedDictionaryConverter<TValue> : JsonConverter<Dictionary<Point, TValue>> 
    {
        /*
        public override Dictionary<Point, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var enumerableConverter = (JsonConverter<IEnumerable<KeyValuePair<Point, TValue>>>)options.GetConverter(typeof(IEnumerable<KeyValuePair<Point, TValue>>));
            return enumerableConverter.Read(ref reader, typeof(IEnumerable<KeyValuePair<Point, TValue>>), options)?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }
        public override void Write(Utf8JsonWriter writer, Dictionary<Point, TValue> value, JsonSerializerOptions options)
        {
            var enumerableConverter = (JsonConverter<IEnumerable<KeyValuePair<Point, TValue>>>)options.GetConverter(typeof(IEnumerable<KeyValuePair<Point, TValue>>));
            enumerableConverter.Write(writer, value, options);
        }
        */
        /*
        public override Dictionary<Point, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var converter = (JsonConverter<Dictionary<string, TValue>>)options.GetConverter(typeof(Dictionary<string, TValue>))!;
            return converter.Read(ref reader, typeof(Dictionary<string, TValue>), options)
                ?.ToDictionary(kv => Point.Parse(kv.Key), kv => kv.Value);
        }
        public override void Write(Utf8JsonWriter writer, Dictionary<Point, TValue> value, JsonSerializerOptions options)
        {
            var converter = (JsonConverter<Dictionary<string, TValue>>)options.GetConverter(typeof(Dictionary<string, TValue>))!;
        }
        */

        public override Dictionary<Point, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            JsonConverter<TValue>? valueConverter = null;
            Dictionary<Point, TValue>? dictionary = null;
            while (reader.Read()) 
            {
                if (reader.TokenType == JsonTokenType.EndObject) 
                {
                    return dictionary;
                }
                valueConverter ??= (JsonConverter<TValue>)options.GetConverter(typeof(TValue))!;
                dictionary ??= [];
                var key = Point.Parse(reader.GetString()!);
                reader.Read();
                var value = valueConverter.Read(ref reader, typeof(TValue), options)!;
                dictionary.Add(key, value);
            }
            return dictionary;
        }

        public override void Write(Utf8JsonWriter writer, Dictionary<Point, TValue> value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            JsonConverter<TValue>? valueConverter = null;
            foreach (var (k, v) in value) 
            {
                valueConverter ??= (JsonConverter<TValue>)options.GetConverter(typeof(TValue));
                writer.WritePropertyName(k.ToString());
                valueConverter.Write(writer, v, options);
            }
            writer.WriteEndObject();
        }
    }
}
